package org.xhy.circuitbreaker.proxy;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.xhy.circuitbreaker.CircuitBreaker;
import org.xhy.circuitbreaker.ICircuitBreakerController;
import org.xhy.circuitbreaker.MethodWrapper;
import org.xhy.circuitbreaker.RequestRateCalculator;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @gitee: https://gitee.com/XhyQAQ
 * @copyright: B站: https://space.bilibili.com/152686439
 * @Author: Xhy
 * @CreateTime: 2024-06-04 16:37
 */
public class CircuitBreakerProxy implements MethodInterceptor {

    private Object object;
    private Set<Integer> methods;

    private Map<Integer, RequestRateCalculator> requestRateMap;

    private ICircuitBreakerController circuitBreakerController;

    private long windowRequestTime;

    public CircuitBreakerProxy(Object bean, Set<Integer> methods, ICircuitBreakerController circuitBreakerController){
        this.object = bean;
        this.methods = methods;
        this.circuitBreakerController = circuitBreakerController;
        this.requestRateMap = new HashMap<>();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        final int hashCode = method.hashCode();
        // 判断是否是熔断方法
        if (methods.contains(hashCode)){
            final CircuitBreaker circuitBreaker = method.getAnnotation(CircuitBreaker.class);
            // 当前时间 + 窗口时间 = end 时间，如果 end 时间大于 设置的 end 时间则重新设置 end时间
            // 下面计算的时候如果当前时间小于 end 时间则沿用，否则就重新创建


            if (circuitBreakerController.exist(hashCode)) {
                final String callbackName = circuitBreaker.callback();
                return this.object.getClass().getMethod(callbackName).invoke(object);
            }
            // 记录
            if (!requestRateMap.containsKey(hashCode)){
                requestRateMap.put(hashCode,new RequestRateCalculator());
            }
            boolean result = true;
            // 执行
            Object returnValue = null;
            // 时间窗口内

            try {
                returnValue = methodProxy.invokeSuper(o, objects);
            }catch (Exception e){
                result = false;
            }finally {
                RequestRateCalculator requestRate = requestRateMap.get(hashCode);

                final long l = circuitBreaker.windowSize();
                final TimeUnit timeUnit = circuitBreaker.timeUnit();
                final long currentTime = System.currentTimeMillis();
                if (currentTime > windowRequestTime){
                    windowRequestTime = timeUnit.toMillis(l) + currentTime;
                    requestRate.reset();
                }
                if (result){
                    requestRate.incrSuccess();
                }else {
                    requestRate.incrFail();
                }
                // 查看比例
                if (requestRate.isSuccessRateWithinLimit(circuitBreaker.criticalFailureRate())) {
                    // 设置真实对象，不能走代理
                    circuitBreakerController.trigger(new MethodWrapper(method,object,requestRate));
                }
            }
            return returnValue;
        }

        return methodProxy.invokeSuper(o, objects);
    }

}
